#include "usbd_core.h"
#include "usb_audio.h"
#include "usbd_audio.h"
#include "usb_midi.h"
#include "main.h"
#define USBD_VID           0x36b7
#define USBD_PID           0xffff
#define USBD_MAX_POWER     50
#define USBD_LANGID_STRING 1033

#define MIDI_IN_EP   0x81
#define MIDI_OUT_EP  0x02
#define AUDIO_OUT_EP 0x03

/* AUDIO Class Config */
#define AUDIO_FREQ 16000U

#define AUDIO_SAMPLE_FREQ(frq) (uint8_t)(frq), (uint8_t)((frq >> 8)), (uint8_t)((frq >> 16))

/* AudioFreq * DataSize (2 bytes) * NumChannels (Stereo: 2) */
/* 16bit(2 Bytes) (Mono:2) */
#define AUDIO_OUT_PACKET ((uint32_t)((AUDIO_FREQ * 2 * 2) / 1000))

#define AUDIO_MS_SIZ (7 + MIDI_SIZEOF_JACK_DESC + 9 + 5 + 9 + 5)

#define USB_AUDIO_CONFIG_DESC_SIZ (unsigned long)(9 +                                       \
                                                  AUDIO_AC_DESCRIPTOR_INIT_LEN(2) +         \
                                                  AUDIO_SIZEOF_AC_INPUT_TERMINAL_DESC +     \
                                                  AUDIO_SIZEOF_AC_FEATURE_UNIT_DESC(2, 1) + \
                                                  AUDIO_SIZEOF_AC_OUTPUT_TERMINAL_DESC +    \
                                                  AUDIO_AS_DESCRIPTOR_INIT_LEN(1) +         \
                                                  AUDIO_MS_STANDARD_DESCRIPTOR_INIT_LEN +   \
                                                  AUDIO_MS_SIZ)

#define AUDIO_AC_SIZ (AUDIO_SIZEOF_AC_HEADER_DESC(2) +          \
                      AUDIO_SIZEOF_AC_INPUT_TERMINAL_DESC +     \
                      AUDIO_SIZEOF_AC_FEATURE_UNIT_DESC(2, 1) + \
                      AUDIO_SIZEOF_AC_OUTPUT_TERMINAL_DESC)
                                                  
const uint8_t midi_descriptor[] = {
    USB_DEVICE_DESCRIPTOR_INIT(USB_2_0, 0x0, 0x0, 0x0, USBD_VID, USBD_PID, 0x0100, 0x01),
    USB_CONFIG_DESCRIPTOR_INIT(USB_AUDIO_CONFIG_DESC_SIZ, 0x03, 0x01, USB_CONFIG_BUS_POWERED, USBD_MAX_POWER),
    AUDIO_AC_DESCRIPTOR_INIT(0x00, 0x03, AUDIO_AC_SIZ, 0x00, 0x01, 0x02),
    AUDIO_AC_INPUT_TERMINAL_DESCRIPTOR_INIT(0x01, AUDIO_TERMINAL_STREAMING, 0x02, 0x0003),
    AUDIO_AC_FEATURE_UNIT_DESCRIPTOR_INIT(0x02, 0x01, 0x01, 0x03, 0x00),
    AUDIO_AC_OUTPUT_TERMINAL_DESCRIPTOR_INIT(0x03, AUDIO_OUTTERM_SPEAKER, 0x02),
    AUDIO_AS_DESCRIPTOR_INIT(0x01, 0x01, 0x02, AUDIO_OUT_EP, AUDIO_OUT_PACKET, 0x1, AUDIO_SAMPLE_FREQ_3B(AUDIO_FREQ)),
    AUDIO_MS_STANDARD_DESCRIPTOR_INIT(0x02, 0x02),
    MIDI_CS_HEADER_DESCRIPTOR_INIT(AUDIO_MS_SIZ),
    MIDI_JACK_DESCRIPTOR_INIT(0x01),
    /* OUT endpoint descriptor */
    0x09, 0x05, MIDI_OUT_EP, 0x02, WBVAL(64), 0x00, 0x00, 0x00,
  
    0x05, /* bLength                                 */
    0x25, /* bDescriptorType: 0x25 (CS_ENDPOINT)     */
    0x01, /* bDescriptorSubtype: 0x01 (MS_GENERAL)   */
    0x01, /* bNumEmbMIDIJack                         */
    0x01, /* baAssocJackID                           */

    /* IN endpoint descriptor */
    0x09, 0x05, MIDI_IN_EP, 0x02, WBVAL(64), 0x00, 0x00, 0x00,
    0x05, 0x25, 0x01, 0x01, 0x03,
    ///////////////////////////////////////
    /// string0 descriptor
    ///////////////////////////////////////
    USB_LANGID_INIT(USBD_LANGID_STRING),
    ///////////////////////////////////////
    /// string1 descriptor
    ///////////////////////////////////////
    0x0A,                       /* bLength */
    USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
    'P', 0x00,                  /* wcChar0 */
    'U', 0x00,                  /* wcChar1 */
    'Y', 0x00,                  /* wcChar2 */
    'A', 0x00,                  /* wcChar3 */
    ///////////////////////////////////////
    /// string2 descriptor
    ///////////////////////////////////////
    0x1E,                       /* bLength */
    USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
    'P', 0x00,                  /* wcChar0 */
    'U', 0x00,                  /* wcChar1 */
    'Y', 0x00,                  /* wcChar2 */
    'A', 0x00,                  /* wcChar3 */
    ' ', 0x00,                  /* wcChar4 */
    'M', 0x00,                  /* wcChar5 */
    'I', 0x00,                  /* wcChar6 */
    'D', 0x00,                  /* wcChar7 */
    'I', 0x00,                  /* wcChar8 */
    ' ', 0x00,                  /* wcChar9 */
    'D', 0x00,                  /* wcChar10 */
    'E', 0x00,                  /* wcChar11 */
    'M', 0x00,                  /* wcChar12 */
    'O', 0x00,                  /* wcChar13 */
    ///////////////////////////////////////
    /// string3 descriptor
    ///////////////////////////////////////
    0x16,                       /* bLength */
    USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */
    '2', 0x00,                  /* wcChar0 */
    '0', 0x00,                  /* wcChar1 */
    '2', 0x00,                  /* wcChar2 */
    '1', 0x00,                  /* wcChar3 */
    '0', 0x00,                  /* wcChar4 */
    '3', 0x00,                  /* wcChar5 */
    '1', 0x00,                  /* wcChar6 */
    '0', 0x00,                  /* wcChar7 */
    '0', 0x00,                  /* wcChar8 */
    '1', 0x00,                  /* wcChar9 */
#ifdef CONFIG_USB_HS
    ///////////////////////////////////////
    /// device qualifier descriptor
    ///////////////////////////////////////
    0x0a,
    USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER,
    0x00,
    0x02,
    0x00,
    0x00,
    0x00,
    0x40,
    0x01,
    0x00,
#endif
    0x00
};

static uint8_t sendbuffer[4];
static uint32_t note_pos;
static volatile uint8_t usb_state_busy;

/* Store example melody as an array of note values */
static const uint8_t note_sequence[] = {
    74, 78, 81, 86, 90, 93, 98, 102, 57, 61, 66, 69, 73, 78, 81, 85, 88, 92, 97, 100, 97, 92, 88, 85, 81, 78,
    74, 69, 66, 62, 57, 62, 66, 69, 74, 78, 81, 86, 90, 93, 97, 102, 97, 93, 90, 85, 81, 78, 73, 68, 64, 61,
    56, 61, 64, 68, 74, 78, 81, 86, 90, 93, 98, 102
};

void usbd_configure_done_callback(void)
{
    
}

void usbd_audio_open(uint8_t intf)
{
    (void)intf;
}
void usbd_audio_close(uint8_t intf)
{
    (void)intf;
}


void usbd_midi_bulk_out(uint8_t ep, uint32_t nbytes)
{
    (void)ep;
    (void)nbytes;
}

void usbd_midi_bulk_in(uint8_t ep, uint32_t nbytes)
{
    (void)ep;
    (void)nbytes;
    usb_state_busy = false;
}

void usbd_audio_out_callback(uint8_t ep, uint32_t nbytes)
{
    (void)ep;
    (void)nbytes;
}

struct usbd_endpoint midi_out_ep = {
    .ep_addr = MIDI_OUT_EP,
    .ep_cb = usbd_midi_bulk_out
};

struct usbd_endpoint midi_in_ep = {
    .ep_addr = MIDI_IN_EP,
    .ep_cb = usbd_midi_bulk_in
};

static struct usbd_endpoint audio_out_ep = {
    .ep_cb = usbd_audio_out_callback,
    .ep_addr = AUDIO_OUT_EP
};

struct usbd_interface intf0;
struct usbd_interface intf1;
struct usbd_interface intf2;

void midi_init()
{
    usbd_desc_register(midi_descriptor);
    usbd_add_interface(usbd_audio_init_intf(&intf0));
    usbd_add_interface(usbd_audio_init_intf(&intf1));
    usbd_add_interface(&intf2);   
    usbd_add_endpoint(&midi_in_ep);
    usbd_add_endpoint(&midi_out_ep);
    usbd_add_endpoint(&audio_out_ep);
  
    usbd_audio_add_entity(0x02, AUDIO_CONTROL_FEATURE_UNIT);

    usbd_initialize();
}

/**
 * @brief MIDI test function, demonstrating how to send MIDI Note On/Off messages via USB
 * 
 * This function demonstrates a simple MIDI auto-playback functionality:
 * 1. Send a Note On message (play note)
 * 2. Send a Note Off message for the previous note (stop previous note)
 * 3. Loop through predefined note sequence
 */
void midi_test()
{
  int ret = 0;

  /* Check if USB device is configured, return if not configured */
  if (usb_device_is_configured() == false) {
      return;
  }

  /* Send Note On message */
  /* Cable num:0, CIN: Note On */
  sendbuffer[0] = (0 << 4) | MIDI_CIN_NOTE_ON;
  /* MIDI status byte: Note On | 0x00 (channel 1) */
  sendbuffer[1] = NoteOn | 0;
  /* Note number */
  sendbuffer[2] = note_sequence[note_pos];
  /* Velocity value, range 0-127, 127 is maximum velocity */
  sendbuffer[3] = 127;
  
  /* Set USB transmission busy flag */
  usb_state_busy = true;
  
  /* Start send MIDI event packet to host */
  ret = usbd_ep_start_write(MIDI_IN_EP, sendbuffer, 4);
  if (ret < 0) {
      printf("error\n");
      return;
  }
  
  /* Busy wait until USB transmission completion callback clears the busy flag */
  while (usb_state_busy) {
  }

  /* Send Note Off message */
  /* Cable num:0, CIN: Note Off */
  sendbuffer[0] = (0 << 4) | MIDI_CIN_NOTE_OFF;
  /* MIDI status byte: Note Off | 0x00 (channel 1) */
  sendbuffer[1] = NoteOff | 0;
  /* Note number do not change */
  /* Velocity value */
  sendbuffer[3] = 0;
  HAL_Delay(100);

  /* Start USB transmission again to send Note Off message */
  usb_state_busy = true;
  ret = usbd_ep_start_write(MIDI_IN_EP, sendbuffer, 4);
  if (ret < 0) {
      printf("error\n");
      return;
  }
  while (usb_state_busy) {
  }
  
  /* Update note sequence position for next note playback */
  note_pos++;
  if (note_pos >= sizeof(note_sequence)) {
      note_pos = 0;  /* Loop playback sequence */
  }
  
}
